/*
 * Copyright (C) 2000-2005 Philips Electronics N.V.
 * 
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or 
 * (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; see the file gnu_license.txt.  If not, write to
 * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  
 * 
 */
#include "cfront.h"
#include "attribute.h"

void
cf_add_attribute (gnu_attr attr, attribute a)
{
  gnu_attr_attrs (attr) = Create_List_attribute (a, gnu_attr_attrs (attr));
}

attr_id
get_attribute(gnu_attr me, Ident name)
{
  List_cfront_attribute ats;
  
  if (me==NULL) return NULL;
  for (ats = gnu_attr_attrs (me); ats; ats = List_attribute_next (ats)) {
    attribute at = List_attribute_data (ats);
    attr_id id;

    if (!at) continue;
    id = attribute_attr (at);
    if (attr_id_tag(id)!=IdAttr_kind) continue;
    if (IdAttr_id(id) == name) return id;
  }
  return NULL;
}

static List_cfront_attribute 
remove_attr (List_cfront_attribute l, Ident name)
{
  attribute at;
  attr_id id;

  if (l==NULL) return l;
  TAIL(l) = remove_attr (TAIL(l), name);
  at = HEAD (l);

  if (!at) return l;
  id = attribute_attr (at);
  if (attr_id_tag(id)!=IdAttr_kind) return l;
  if (IdAttr_id(id) == name) return TAIL(l);
  return l;
}

gnu_attr
remove_attribute (gnu_attr me, Ident name)
{
  if (me==NULL) return me;
  gnu_attr_attrs(me) = remove_attr (gnu_attr_attrs(me), name);
  if (!gnu_attr_attrs(me)) return NULL;
  return me;
}

/* ----------------------------------------------------------------------------
 * Find custom_op attribute
 */
Bool
is_custom_op (expression e)
{
  declaration decl;
  declarator d;
  Type t;
#warning I dont like cfront using the attributes, can we do this in some other way?
  
  if (expression_tag(e) != IdExpr_kind) return FALSE;
  t = expression_type(e);
  if (t && Type_tag(t)!=SpecType_kind) {
    return get_attribute (SpecType_attr(t), Create_Ident("custom_op")) != NULL;
  }

  /* No type (yet), look at decl */
  d = IdExpr_decl(e);
  if (!d) return FALSE;
  decl = declarator_my_decl(d);
  if (!decl) return FALSE;
  d = HEAD(declaration_decls(decl));
  if (!d) return FALSE;
  if (declarator_tag(d) != FuncDecl_kind) return FALSE;
  return get_attribute (FuncDecl_attrs(d), Create_Ident("custom_op")) != NULL;
}

/* ----------------------------------------------------------------------------
 * Parse expression attributes
 */
static void
parse_attribute_initDeclarator(initDeclarator dtor)
{
  if (!user_parse_expression_attribute) return;
  initDeclarator_attr(dtor) = user_parse_expression_attribute (initDeclarator_attr(dtor));
}

static void
parse_attribute_ParExpr(ParExpr expr)
{
  if (!user_parse_expression_attribute) return;
  ParExpr_attr(expr) = user_parse_expression_attribute (ParExpr_attr(expr));
}

static void
parse_attribute_ExprStat(ExprStat expr)
{
  if (!user_parse_expression_attribute) return;
  ExprStat_attr(expr) = user_parse_expression_attribute (ExprStat_attr(expr));
}

static void
parse_attribute_compound_statement(compound_statement stat)
{
  if (!user_parse_statement_attribute) return;
  compound_statement_attr(stat) = user_parse_statement_attribute (compound_statement_attr(stat));
}

void
ast_parse_attributes (void *node, void trav_void(void *, void *))
{
  static struct s_void_cfront_TraverseTable parse_attribute;

  parse_attribute.action_initDeclarator[POST_ACTION] = parse_attribute_initDeclarator;
  parse_attribute.action_ParExpr[POST_ACTION]  = parse_attribute_ParExpr;
  parse_attribute.action_ExprStat[POST_ACTION] = parse_attribute_ExprStat;
  parse_attribute.action_compound_statement[POST_ACTION] = parse_attribute_compound_statement;
  trav_void (node, &parse_attribute);
}
